package com.xnx.bincker.leader.utils;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.ObjectWriter;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.xnx.bincker.leader.core.aop.ResponsePojoIgnorePropertyMixinGenerator;
import javassist.CannotCompileException;
import org.springframework.beans.BeanUtils;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsUtils;
import org.springframework.web.cors.DefaultCorsProcessor;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.beans.PropertyDescriptor;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 常用工具类
 */
public class CommonUtils {
    /**
     * 返回前台JSON数据,并过滤字段
     * @param request request对象
     * @param response response对象
     * @param content 返回内容对象
     * @param ignoreProperties 过滤字段表
     */
    @SafeVarargs
    public static void responseJson(
            HttpServletRequest request,
            HttpServletResponse response,
            Object content,
            Pair<Class<?>, Set<String>> ...ignoreProperties
    ) throws IOException {
//        写出cors头信息
        if(CorsUtils.isCorsRequest(request)){
            RequestMappingHandlerMapping requestMappingHandlerMapping = SpringUtils.getBean(RequestMappingHandlerMapping.class);
            Map<String, CorsConfiguration> corsConfigurationMap = requestMappingHandlerMapping.getCorsConfigurations();
            DefaultCorsProcessor corsProcessor = new DefaultCorsProcessor();
            for(Map.Entry<String, CorsConfiguration> item : corsConfigurationMap.entrySet()){
                AntPathRequestMatcher matcher = new AntPathRequestMatcher(item.getKey());
                if(matcher.matches(request)){
                    if(corsProcessor.processRequest(item.getValue(), request, response)) break;
                }
            }
        }
//        生成JSON过滤混淆
        Map<Class<?>, Class<?>> mixinMap = new HashMap<>();
        if(ignoreProperties.length > 0){
            try {
                for (Pair<Class<?>, Set<String>> ignoreProperty : ignoreProperties) {
                    Class<?> mixinInterface = ResponsePojoIgnorePropertyMixinGenerator.generateJsonMixinInterface(
                            ignoreProperty.getFirst(),
                            ignoreProperty.getSecond()
                    );
                    mixinMap.put(ignoreProperty.getFirst(), mixinInterface);
                }
            } catch (CannotCompileException e) {
                throw new RuntimeException("获取POJO属性过滤对象失败", e);
            }
        }
//        写出数据
        ObjectWriter writer =
                SpringUtils.getBean(Jackson2ObjectMapperBuilder.class)
                        .build()
                        .setMixIns(mixinMap)
                        .writer();
        response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
        writer.writeValue(response.getWriter(), content);
    }

    /**
     * 返回JSON数据到前台，并设置只输出的属性
     * @param request request对象
     * @param response response对象
     * @param content 内容
     * @param includeProperties 包含属性信息
     */
    @SafeVarargs
    public static void responseJsonLimit(HttpServletRequest request, HttpServletResponse response, Object content, Pair<Class<?>, Set<String>> ...includeProperties) throws IOException {
//        把包含转换为不包含
        for (Pair<Class<?>, Set<String>> includeProperty : includeProperties) {
            Set<String> includeFields = includeProperty.getSecond();
            includeProperty.setSecond(
                    Arrays.stream(BeanUtils.getPropertyDescriptors(includeProperty.getFirst()))
                    .map(PropertyDescriptor::getName)
                    .filter(fieldName->!includeFields.contains(fieldName))
                    .collect(Collectors.toSet())
            );
        }
        responseJson(request, response, content, includeProperties);
    }
}
